{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import mxnet as mx\n",
    "from mxnet import init, gluon, test_utils, autograd, nd\n",
    "from mxnet.gluon import nn, utils\n",
    "import numpy as np\n",
    "import gzip, struct\n",
    "from matplotlib import pyplot as plt\n",
    "from time import time\n",
    "%matplotlib inline\n",
    "ctx = mx.gpu()\n",
    "data_dir = '/home/sinyer/python/data/mnist'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_mnist():\n",
    "    def read_data(label_dir, image_dir):\n",
    "        with gzip.open(label_dir) as flbl:\n",
    "            struct.unpack(\">II\", flbl.read(8))\n",
    "            label = np.fromstring(flbl.read(), dtype=np.int8)\n",
    "        with gzip.open(image_dir, 'rb') as fimg:\n",
    "            _, _, rows, cols = struct.unpack(\">IIII\", fimg.read(16))\n",
    "            image = np.fromstring(fimg.read(), dtype=np.uint8).reshape(len(label), rows, cols)\n",
    "            image = image.reshape(image.shape[0], 1, 28, 28).astype(np.float32)/255\n",
    "        return label, image\n",
    "    train_label, train_img = read_data(\n",
    "        data_dir+'/train-labels-idx1-ubyte.gz', data_dir+'/train-images-idx3-ubyte.gz')\n",
    "    test_label, test_img = read_data(\n",
    "        data_dir+'/t10k-labels-idx1-ubyte.gz', data_dir+'/t10k-images-idx3-ubyte.gz')\n",
    "    return train_img, train_label, test_img, test_label\n",
    "\n",
    "def visualize(img_arr):\n",
    "    plt.imshow((img_arr.asnumpy().reshape(28,28)*255).astype(np.uint8), cmap='gray')\n",
    "    plt.axis('off')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_img, train_label, test_img, test_label = load_mnist()\n",
    "train_label = nd.one_hot(nd.array(train_label), 10).astype(np.float32)\n",
    "\n",
    "batch_size = 64\n",
    "train_iter = mx.io.NDArrayIter(data=train_img, label=train_label, batch_size=batch_size)\n",
    "real_label = nd.ones((batch_size,), ctx=ctx)\n",
    "fake_label = nd.zeros((batch_size,),ctx=ctx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "netG = nn.HybridSequential()\n",
    "with netG.name_scope():\n",
    "    netG.add(nn.Dense(units=128, activation='relu', weight_initializer=init.Xavier()))\n",
    "    netG.add(nn.Dense(units=784, activation='sigmoid', weight_initializer=init.Xavier()))\n",
    "\n",
    "netD = nn.HybridSequential()\n",
    "with netD.name_scope():\n",
    "    netD.add(nn.Dense(units=128, activation='relu', weight_initializer=init.Xavier()))\n",
    "    netD.add(nn.Dense(units=1, weight_initializer=init.Xavier()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "netG.initialize(ctx=ctx)\n",
    "netD.initialize(ctx=ctx)\n",
    "\n",
    "loss = gluon.loss.SigmoidBinaryCrossEntropyLoss()\n",
    "trainerG = gluon.Trainer(netG.collect_params(), 'adam', {'learning_rate': 0.001})\n",
    "trainerD = gluon.Trainer(netD.collect_params(), 'adam', {'learning_rate': 0.001})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0, dloss 0.0197, gloss 6.3894, T 3.1772\n",
      "20, dloss 0.7756, gloss 1.4742, T 3.3712\n",
      "40, dloss 0.7352, gloss 1.6385, T 3.1249\n",
      "60, dloss 0.7047, gloss 1.4895, T 3.1290\n",
      "80, dloss 0.7105, gloss 1.6441, T 3.3069\n"
     ]
    }
   ],
   "source": [
    "epochs = 100\n",
    "\n",
    "for epoch in range(epochs):\n",
    "    start = time()\n",
    "    train_iter.reset()\n",
    "    for batch in train_iter:\n",
    "        data = batch.data[0].as_in_context(ctx)\n",
    "        label = batch.label[0].as_in_context(ctx)\n",
    "        latent_z = mx.nd.random_normal(0, 1, shape=(batch_size, 100), ctx=ctx)\n",
    "        D_input = nd.concat(data.reshape((data.shape[0], -1)), label)\n",
    "        G_input = nd.concat(latent_z, label)   \n",
    "        with autograd.record():\n",
    "            output = netD(D_input)\n",
    "            errD_real = loss(output, real_label)\n",
    "            fake = netG(G_input)\n",
    "            D_fake_input = nd.concat(fake.reshape((fake.shape[0], -1)), label)\n",
    "            output = netD(D_fake_input.detach())\n",
    "            errD_fake = loss(output, fake_label)\n",
    "            errD = errD_real + errD_fake\n",
    "            errD.backward()\n",
    "        trainerD.step(data[0].shape[0])\n",
    "        with autograd.record():\n",
    "            fake = netG(G_input)\n",
    "            D_fake_input = nd.concat(fake.reshape((fake.shape[0], -1)), label)\n",
    "            output = netD(D_fake_input)\n",
    "            errG = loss(output, real_label)\n",
    "            errG.backward()\n",
    "        trainerG.step(data[0].shape[0])\n",
    "\n",
    "    if epoch%20 == 0:\n",
    "        print('%d, dloss %.4f, gloss %.4f, T %.4f' %(\n",
    "            epoch, nd.mean(errD).asscalar(), nd.mean(errG).asscalar(), time()-start))\n",
    "print('%d, dloss %.4f, gloss %.4f' %(epoch, nd.mean(errD).asscalar(), nd.mean(errG).asscalar())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 16 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "num_image = 4\n",
    "for digit in range(4):\n",
    "    for i in range(num_image):\n",
    "        latent_z = mx.nd.random_normal(0, 1, shape=(1, 100), ctx=ctx)\n",
    "        label = nd.one_hot(nd.array([[digit]]), 10).as_in_context(ctx)\n",
    "        img = netG(nd.concat(latent_z, label.reshape((1, 10))))\n",
    "        plt.subplot(4, 4, digit * 4 + i + 1)\n",
    "        visualize(img[0])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
